﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Carbon.Web.Forms;
using Carbon.Web.Forms.jQueryForms;

namespace Demo.Demos.ComplexDataStructures
{
    [Serializable]
    public class PersonViewModel
    {
        public PersonViewModel(ComplexDataStructuresViewModel rootViewModel)
        {
            Subordinates = new List<PersonViewModel>();
            Friends = new List<PersonViewModel>();
            RootViewModel = rootViewModel;
            IsNodeExpanded = true;
        }

        // hierarchy properties
        public ComplexDataStructuresViewModel RootViewModel { get; private set; }
        public PersonViewModel Parent { get; internal set; }       
        public List<PersonViewModel> Subordinates { get; private set; }

        // data properties
        public String Name { get; set; }
        public String AvatarPicture { get; set; }
        public FileReference CustomAvatarPicture { get; set; }
        public List<PersonViewModel> Friends { get; private set; }

        // display properties
        public bool IsNodeExpanded { get; set; }

        public String DisplayName
        {
            get
            {
                if (string.IsNullOrEmpty(Name))
                    return string.Format("<Empty> ({0:0.00})", PowerFactor);
                else
                    return string.Format("{0} ({1:0.00})", Name, PowerFactor);
            }
        }

        public bool IsSelected
        {
            get
            {
                return RootViewModel.SelectedPerson == this;
            }
        }

        public int HierarchyLevel
        {
            get 
            {
                int level = 1;
                PersonViewModel root = this;
                while (root.Parent != null && level < 100)
                {
                    root = root.Parent;
                    level++;
                }

                return level;
            }
        }

        public IEnumerable<FriendWrapperViewModel> DisplayFriends
        {
            get
            {
                return Friends.Select(i => new FriendWrapperViewModel(this, i));
            }
        }

        public DropDownListItemCollection<PersonViewModel, PersonViewModel> AddNewFriendPickerDropDown
        {
            get
            {
                List<PersonViewModel> allPersons = new List<PersonViewModel>();
                this.RootViewModel.RootPerson.Visitor(i => { if (i != this && !this.Friends.Contains(i)) allPersons.Add(i); });
                return new DropDownListItemCollection<PersonViewModel, PersonViewModel>(allPersons, i => i, i => i.DisplayName, null, true);
            }
            set
            {
                if (value.SelectedItem != null)
                {
                    this.Friends.Add(value.SelectedItem);
                }
            }
        }

        // calculated properties
        public float PowerFactor
        {
            get
            {
                float factor = 0;

                factor += (float)Math.Pow((1.0f / RootViewModel.LevelFactor), HierarchyLevel);

                foreach (PersonViewModel friendViewModel in Friends)
                {
                    // mutual
                    if (friendViewModel.Friends.Contains(this))
                    {
                        factor += RootViewModel.FriendFactor * (float)Math.Pow(1.0f / RootViewModel.LevelFactor, friendViewModel.HierarchyLevel);
                    }
                    else
                    {
                        factor -= RootViewModel.OneSideFriendFactor * (float)Math.Pow(1.0f / RootViewModel.LevelFactor, friendViewModel.HierarchyLevel);
                    }
                }

                // add all those that are not listed as my friends but have listed me
                RootViewModel.RootPerson.Visitor((i) =>
                    {
                        if (i != this && !this.Friends.Contains(i) && i.Friends.Contains(this))
                        {
                            factor += RootViewModel.OneSideFriendFactor * (float)Math.Pow(1.0f / RootViewModel.LevelFactor, i.HierarchyLevel);
                        }
                    });

                return factor;
            }
        }

        // actions
        public void SelectCurrent()
        {
            RootViewModel.SelectedPerson = this;
        }

        public void AddNewSubordinate(params PersonViewModel[] persons)
        {
            foreach (PersonViewModel person in persons)
            {
                person.Parent = this;
                if ((person != this) == false)  throw new InvalidOperationException("(person != this) == false");
                if (!Subordinates.Contains(person))
                    Subordinates.Add(person);
            }
        }

        public void AddNewFriends(params PersonViewModel[] persons)
        {
            foreach (PersonViewModel person in persons)
            {
                if ((person != this) == false)  throw new InvalidOperationException("(person != this) == false");
                if (!Friends.Contains(person))
                    Friends.Add(person);
            }
        }

        private void DeleteInternal()
        {
            if (RootViewModel.SelectedPerson == this)
            {
                RootViewModel.SelectedPerson = null;
            }

            RootViewModel.RootPerson.Visitor(i =>
                {
                    i.Friends.Remove(this);
                });
        }

        public void Delete()
        {
            if (this.Parent == null)  throw new NullReferenceException("this.Parent");
            this.Parent.Subordinates.Remove(this);

            this.Visitor((i) =>
                {
                    i.DeleteInternal();
                });
        }

        public void Visitor(Action<PersonViewModel> personAction)
        {
            foreach (PersonViewModel person in this.Subordinates)
            {
                person.Visitor(personAction);
            }

            personAction(this);
        }
    }
}